{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tensor and Variable\n",
    "这是 PyTorch 基础的第二课，通过本次课程，你能够学会如何像使用 NumPy 一样使用 PyTorch，了解到 PyTorch 中的基本元素 Tensor 和 Variable 及其操作方式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 把 PyTorch 当做 NumPy 用\n",
    "PyTorch 的官方介绍是一个拥有强力GPU加速的张量和动态构建网络的库，其主要构件是张量，所以我们可以把 PyTorch 当做 NumPy 来用，PyTorch 的很多操作好 NumPy 都是类似的，但是因为其能够在 GPU 上运行，所以有着比 NumPy 快很多倍的速度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 创建一个 numpy ndarray\n",
    "numpy_tensor = np.random.randn(10, 20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以使用下面两种方式将numpy的ndarray转换到tensor上"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "pytorch_tensor1 = torch.Tensor(numpy_tensor)\n",
    "pytorch_tensor2 = torch.from_numpy(numpy_tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用以上两种方法进行转换的时候，会直接将 NumPy ndarray 的数据类型转换为对应的 PyTorch Tensor 数据类型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "同时我们也可以使用下面的方法将 pytorch tensor 转换为 numpy ndarray"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 如果 pytorch tensor 在 cpu 上\n",
    "numpy_array = pytorch_tensor1.numpy()\n",
    "\n",
    "# 如果 pytorch tensor 在 gpu 上\n",
    "numpy_array = pytorch_tensor1.cpu().numpy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "需要注意 GPU 上的 Tensor **不能直接转换为 NumPy ndarray** ，需要使用`.cpu()`先将 GPU 上的 Tensor 转到 CPU 上"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PyTorch Tensor 使用 GPU 加速\n",
    "\n",
    "我们可以使用以下两种方式将 Tensor 放到 GPU 上"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 第一种方式是定义 cuda 数据类型\n",
    "dtype = torch.cuda.FloatTensor # 定义默认 GPU 的 数据类型\n",
    "gpu_tensor = torch.randn(10, 20).type(dtype)\n",
    "\n",
    "# 第二种方式更简单，推荐使用\n",
    "gpu_tensor = torch.randn(10, 20).cuda(0) # 将 tensor 放到第一个 GPU 上\n",
    "gpu_tensor = torch.randn(10, 20).cuda(1) # 将 tensor 放到第二个 GPU 上"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用第一种方式将 tensor 放到 GPU 上的时候会将数据类型转换成定义的类型，而是用第二种方式能够直接将 tensor 放到 GPU 上，类型跟之前保持一致\n",
    "\n",
    "推荐在定义 tensor 的时候就明确数据类型，然后直接使用第二种方法将 tensor 放到 GPU 上"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "而将 tensor 放回 CPU 的操作非常简单"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "cpu_tensor = gpu_tensor.cpu()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们也能够访问到 Tensor 的一些属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([10, 20])\n",
      "torch.Size([10, 20])\n"
     ]
    }
   ],
   "source": [
    "# 可以通过下面两种方式得到 tensor 的大小\n",
    "print(pytorch_tensor1.shape)\n",
    "print(pytorch_tensor1.size())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.FloatTensor\n"
     ]
    }
   ],
   "source": [
    "# 得到 tensor 的数据类型\n",
    "print(pytorch_tensor1.type())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n"
     ]
    }
   ],
   "source": [
    "# 得到 tensor 的维度\n",
    "print(pytorch_tensor1.dim())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "200\n"
     ]
    }
   ],
   "source": [
    "# 得到 tensor 的所有元素个数\n",
    "print(pytorch_tensor1.numel())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**小练习**\n",
    "\n",
    "查阅以下[文档](http://pytorch.org/docs/0.3.0/tensors.html)了解 tensor 的数据类型，创建一个 float64、大小是 3 x 2、随机初始化的 tensor，将其转化为 numpy 的 ndarray，输出其数据类型\n",
    "\n",
    "参考输出: float64"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "float64\n"
     ]
    }
   ],
   "source": [
    "x = torch.randn(3,2)\n",
    "# 改变数据类型\n",
    "x = x.type(torch.DoubleTensor)\n",
    "\n",
    "x_array = x.numpy()\n",
    "print(x_array.dtype)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "float64\n"
     ]
    }
   ],
   "source": [
    "# 答案\n",
    "x = torch.randn(3, 2)\n",
    "x = x.type(torch.DoubleTensor)\n",
    "x_array = x.numpy()\n",
    "print(x_array.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tensor的操作\n",
    "Tensor 操作中的 api 和 NumPy 非常相似，如果你熟悉 NumPy 中的操作，那么 tensor 基本是一致的，下面我们来列举其中的一些操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " 1  1\n",
      " 1  1\n",
      "[torch.FloatTensor of size 2x2]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "x = torch.ones(2, 2)\n",
    "print(x) # 这是一个float tensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.FloatTensor\n"
     ]
    }
   ],
   "source": [
    "print(x.type())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " 1  1\n",
      " 1  1\n",
      "[torch.LongTensor of size 2x2]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 将其转化为整形\n",
    "x = x.long()\n",
    "# x = x.type(torch.LongTensor)\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " 1  1\n",
      " 1  1\n",
      "[torch.FloatTensor of size 2x2]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 再将其转回 float\n",
    "x = x.float()\n",
    "# x = x.type(torch.FloatTensor)\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "-0.8203 -0.0328  1.8283\n",
      "-0.1734 -0.1873  0.9818\n",
      "-1.8368 -2.2450 -0.4410\n",
      "-0.8005 -2.1132  0.7140\n",
      "[torch.FloatTensor of size 4x3]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "x = torch.randn(4, 3)\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 沿着行取最大值\n",
    "max_value, max_idx = torch.max(x, dim=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\n",
       " 1.8283\n",
       " 0.9818\n",
       "-0.4410\n",
       " 0.7140\n",
       "[torch.FloatTensor of size 4]"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 每一行的最大值\n",
    "max_value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\n",
       " 2\n",
       " 2\n",
       " 2\n",
       " 2\n",
       "[torch.LongTensor of size 4]"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 每一行最大值的下标\n",
    "max_idx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " 0.9751\n",
      " 0.6212\n",
      "-4.5228\n",
      "-2.1997\n",
      "[torch.FloatTensor of size 4]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 沿着行对 x 求和\n",
    "sum_x = torch.sum(x, dim=1)\n",
    "print(sum_x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([4, 3])\n",
      "torch.Size([1, 4, 3])\n"
     ]
    }
   ],
   "source": [
    "# 增加维度或者减少维度\n",
    "print(x.shape)\n",
    "x = x.unsqueeze(0) # 在第一维增加\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 1, 4, 3])\n"
     ]
    }
   ],
   "source": [
    "x = x.unsqueeze(1) # 在第二维增加\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 4, 3])\n"
     ]
    }
   ],
   "source": [
    "x = x.squeeze(0) # 减少第一维\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([4, 3])\n"
     ]
    }
   ],
   "source": [
    "x = x.squeeze() # 将 tensor 中所有的一维全部都去掉\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([3, 4, 5])\n",
      "torch.Size([4, 3, 5])\n",
      "torch.Size([5, 3, 4])\n"
     ]
    }
   ],
   "source": [
    "x = torch.randn(3, 4, 5)\n",
    "print(x.shape)\n",
    "\n",
    "# 使用permute和transpose进行维度交换\n",
    "x = x.permute(1, 0, 2) # permute 可以重新排列 tensor 的维度\n",
    "print(x.shape)\n",
    "\n",
    "x = x.transpose(0, 2)  # transpose 交换 tensor 中的两个维度\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([3, 4, 5])\n",
      "torch.Size([12, 5])\n",
      "torch.Size([3, 20])\n"
     ]
    }
   ],
   "source": [
    "# 使用 view 对 tensor 进行 reshape\n",
    "x = torch.randn(3, 4, 5)\n",
    "print(x.shape)\n",
    "\n",
    "x = x.view(-1, 5) # -1 表示任意的大小，5 表示第二维变成 5\n",
    "print(x.shape)\n",
    "\n",
    "x = x.view(3, 20) # 重新 reshape 成 (3, 20) 的大小\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x = torch.randn(3, 4)\n",
    "y = torch.randn(3, 4)\n",
    "\n",
    "# 两个 tensor 求和\n",
    "z = x + y\n",
    "# z = torch.add(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "另外，pytorch中大多数的操作都支持 inplace 操作，也就是可以直接对 tensor 进行操作而不需要另外开辟内存空间，方式非常简单，一般都是在操作的符号后面加`_`，比如"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([3, 3])\n",
      "torch.Size([1, 3, 3])\n",
      "torch.Size([3, 1, 3])\n"
     ]
    }
   ],
   "source": [
    "x = torch.ones(3, 3)\n",
    "print(x.shape)\n",
    "\n",
    "# unsqueeze 进行 inplace\n",
    "x.unsqueeze_(0)\n",
    "print(x.shape)\n",
    "\n",
    "# transpose 进行 inplace\n",
    "x.transpose_(1, 0)\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x = torch.ones(3, 3)\n",
    "y = torch.ones(3, 3)\n",
    "print(x)\n",
    "\n",
    "# add 进行 inplace\n",
    "x.add_(y)\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**小练习**\n",
    "\n",
    "访问[文档](http://pytorch.org/docs/0.3.0/tensors.html)了解 tensor 更多的 api，实现下面的要求\n",
    "\n",
    "创建一个 float32、4 x 4 的全为1的矩阵，将矩阵正中间 2 x 2 的矩阵，全部修改成2\n",
    "\n",
    "参考输出\n",
    "$$\n",
    "\\left[\n",
    "\\begin{matrix}\n",
    "1 & 1 & 1 & 1 \\\\\n",
    "1 & 2 & 2 & 1 \\\\\n",
    "1 & 2 & 2 & 1 \\\\\n",
    "1 & 1 & 1 & 1\n",
    "\\end{matrix}\n",
    "\\right] \\\\\n",
    "[torch.FloatTensor\\ of\\ size\\ 4x4]\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1., 1., 1., 1.],\n",
      "        [1., 2., 2., 1.],\n",
      "        [1., 2., 2., 1.],\n",
      "        [1., 1., 1., 1.]])\n"
     ]
    }
   ],
   "source": [
    "x = torch.ones(4,4).float()\n",
    "x[1:3,1:3] = 2\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " 1  1  1  1\n",
      " 1  2  2  1\n",
      " 1  2  2  1\n",
      " 1  1  1  1\n",
      "[torch.FloatTensor of size 4x4]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 答案\n",
    "x = torch.ones(4, 4).float()\n",
    "x[1:3, 1:3] = 2\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Variable\n",
    "tensor 是 PyTorch 中的完美组件，但是构建神经网络还远远不够，我们需要能够构建计算图的 tensor，这就是 Variable。Variable 是对 tensor 的封装，操作和 tensor 是一样的，但是每个 Variabel都有三个属性，Variable 中的 tensor本身`.data`，对应 tensor 的梯度`.grad`以及这个 Variable 是通过什么方式得到的`.grad_fn`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 通过下面这种方式导入 Variable\n",
    "from torch.autograd import Variable"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "需要梯度的话，显式指定"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "x_tensor = torch.randn(10, 5)\n",
    "y_tensor = torch.randn(10, 5)\n",
    "\n",
    "# 将 tensor 变成 Variable\n",
    "x = Variable(x_tensor, requires_grad=True) # 默认 Variable 是不需要求梯度的，所以我们用这个方式申明需要对其进行求梯度\n",
    "y = Variable(y_tensor, requires_grad=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([-1.4663, -3.5878, -2.3401,  1.2809,  0.6121])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 在一个维度上求和\n",
    "torch.sum(x_tensor,dim=0)\n",
    "# y_tensor\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "z = torch.sum(x + y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(-4.8215)\n",
      "<SumBackward0 object at 0x00000167D1D11D30>\n"
     ]
    }
   ],
   "source": [
    "print(z.data)\n",
    "print(z.grad_fn)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面我们打出了 z 中的 tensor 数值，同时通过`grad_fn`知道了其是通过 Sum 这种方式得到的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.]])\n",
      "tensor([[1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.],\n",
      "        [1., 1., 1., 1., 1.]])\n"
     ]
    }
   ],
   "source": [
    "# 求 x 和 y 的梯度\n",
    "z.backward()\n",
    "\n",
    "print(x.grad)\n",
    "print(y.grad)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过`.grad`我们得到了 x 和 y 的梯度，这里我们使用了 PyTorch 提供的自动求导机制，非常方便，下一小节会具体讲自动求导。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**小练习**\n",
    "\n",
    "尝试构建一个函数 $y = 2x^2 $，然后求 x=2 的导数。\n",
    "\n",
    "参考输出：8"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "提示：\n",
    "\n",
    "$y = 2 \\times x^2$的图像如下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "x = np.arange(-3, 3.01, 0.1)\n",
    "y = 2*x ** 2\n",
    "plt.plot(x, y)\n",
    "plt.plot(2, 8, 'ro')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([8.])\n"
     ]
    }
   ],
   "source": [
    "# 答案\n",
    "x = Variable(torch.FloatTensor([2]), requires_grad=True)\n",
    "y = 2*x ** 2\n",
    "y.backward()\n",
    "print(x.grad)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下一次课程我们将会从导数展开，了解 PyTorch 的自动求导机制"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "07b2cea26089ea0ebec7cc3a83022d31e9f80d0db55f432f89380becb3d80933"
  },
  "kernelspec": {
   "display_name": "mx",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
